from enum import Enum

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi import status
from fastapi.responses import JSONResponse
from fastapi.requests import Request
from pydantic import ValidationError

from utils.template.template import template


class StatusCode(Enum):
    """
    自定义状态码枚举类
    """
    SourceNotFound = (404, "资源未找到")
    UnprocessableEntityError = (422, "请求参数错误")
    DataValidationFailed = (423, "Pydantic数据验证失败")
    Success = (1000, "成功")
    UnknownError = (1001, "未知错误")
    QuerySideInfoError = (1002, "查询参数表全部数据错误")
    QueryAllProductTypeError = (1003, "查询产品分类表全部数据错误")
    QuerySlideshowError = (1004, "查询轮播图数据错误")
    QueryArgTypeError = (1005, "数据库查询字段类型错误")
    QueryProductError = (1006, "查询产品数据错误")
    QueryAllNewsTypeError = (1006, "查询新闻分类表全部数据错误")
    SearchKeywordEmpty = (1007, "搜索关键字为空")
    QueryAboutTextError = (1008, "查询`关于我们`文本错误")
    QueryAboutImagesError = (1009, "查询`关于我们`图片错误")
    ProductTypeNotFound = (1010, "未找到指定产品分类")
    ProductByIdNotFound = (1011, "未找到指定ID的产品")
    QueryNewsError = (1012, "查询新闻数据错误")
    NewsTypeNotFound = (1013, "未找到指定新闻分类")
    NewsByIdNotFound = (1014, "未找到指定ID的新闻")
    MessageContentError = (1015, "留言内容错误")
    VerifyCodeError = (1016, "验证码错误")
    ContactInformationError = (1017, "联系信息错误")
    QueryMessageError = (1018, "查询留言数据错误")
    AddMessageError = (1019, "新增留言数据错误")
    MessageLimitError = (1020, "留言数量已达上限")


class AppException(Exception):
    """
    自定义异常类
    """

    def __init__(self, code: StatusCode, message: str = None, detail: str = None):
        self.code = code.value[0]
        self.message = message if message else code.value[1]
        self.detail = detail

    def __str__(self):
        return f"{self.code}: {self.message} - {self.detail}"

    def __repr__(self):
        return self.__str__()


def fastapi_exception_handler(app: FastAPI):
    """
    该函数可用于拦截 FastAPI 应用程序中的所有异常。
    """

    def error_handler(request: Request, e: AppException, http_status: int, output_logger=True):
        response_type = "web"
        if request.scope["path"].startswith("/api/"):
            response_type = "api"

        if output_logger:
            logger = request.state.logger
            logger.exception(e)

        if response_type == "api":
            return JSONResponse(
            content=jsonable_encoder({
                "request_id": request.state.request_id,
                "code": e.code,
                "message": e.message,
                "detail": e.detail
            }), status_code=http_status,)
        elif response_type == "web":
            return template(request, "error.html", context={
                "path": request.url.path,
                "request_id": request.state.request_id,
                "code": e.code,
                "message": e.message,
                "detail": e.detail
            }, status_code=http_status)

    @app.exception_handler(status.HTTP_404_NOT_FOUND)
    async def not_found_exception_handler(request: Request, e: Exception):
        e = AppException(code=StatusCode.SourceNotFound, detail=str(e))

        return error_handler(request, e, status.HTTP_404_NOT_FOUND, output_logger=False)

    @app.exception_handler(AppException)
    async def app_exception_handler(request: Request, e: AppException):
        return error_handler(request, e, status.HTTP_500_INTERNAL_SERVER_ERROR)

    @app.exception_handler(ValidationError)
    async def validation_exception_handler(request: Request, e: RequestValidationError):
        e = AppException(code=StatusCode.DataValidationFailed, detail=str(e.errors()))

        return error_handler(request, e, status.HTTP_500_INTERNAL_SERVER_ERROR)

    @app.exception_handler(RequestValidationError)
    async def request_validation_exception_handler(request: Request, e: RequestValidationError):
        e = AppException(code=StatusCode.UnprocessableEntityError, detail=str(e.errors()))

        return error_handler(request, e, status.HTTP_422_UNPROCESSABLE_ENTITY)

    @app.exception_handler(Exception)
    async def exception_handler(request: Request, e: Exception):
        e = AppException(code=StatusCode.UnknownError, detail=str(e))

        return error_handler(request, e, status.HTTP_500_INTERNAL_SERVER_ERROR)
